# -*- coding: utf-8 -*-
import threading
import time
import traceback

import msgpack
import zmq

from global_data.share_data import ShareData
from utils.icloudp_log import IcloudpLog


class Message(object):
    def __init__(self, message_type, data, node_id):
        self.type = message_type
        self.data = data
        self.node_id = node_id

    def serialize(self):
        return msgpack.dumps((self.type, self.data, self.node_id))

    @classmethod
    def unserialize(cls, data):
        msg = cls(*msgpack.loads(data))
        return msg


class BaseSocket(object):
    def __init__(self):
        self.share_data = ShareData()
        self.log = IcloudpLog.get_logger_zmq()
        self.is_run = False
        self.run_type = 4
        self.host = ""
        self.port = ""
        self.topic = ""
        self.context = None
        self.receiver = None
        self.sender = None
        self.handle = None

    # f发布
    def send(self, msg):
        self.sender.send(msg.serialize())

    # 订阅
    def recv(self):
        data = self.receiver.recv()
        if self.handle:
            self.log.info(f"执行处理函数，返回数据封装为message对象")
            self.handle(Message.unserialize(data))
        return Message.unserialize(data)

    # f发布
    def send_multi(self, topic, msg):
        if type(msg) == str:
            msg = Message("icloudp", msg, None)
        self.sender.send_multipart([topic.encode(), msg.serialize()])

    # 订阅
    def recv_multi(self):
        # self.receiver.setsockopt_string(zmq.SUBSCRIBE, self.topic)
        data = self.receiver.recv_multipart()
        topic = data[0].decode()
        content = data[1]
        if self.handle:
            self.log.info(f"执行主题={self.topic}的处理函数，返回数据封装为message对象")
            self.handle(Message.unserialize(content))
        return Message.unserialize(content)

    # f发布
    def send_str(self, msg):
        self.sender.send(msg)

    # 订阅
    def recv_str(self):
        data = self.receiver.recv()
        if self.handle:
            self.log.info(f"执行订阅的处理函数")
            self.handle(data.encode("utf-8"))
        return data.encode("utf-8")

    # f发布
    def send_multi_str(self, topic, msg):
        self.sender.send_multipart([topic.encode("utf-8"), msg.encode("utf-8")])

    # 订阅
    def recv_multi_str(self):
        # self.receiver.setsockopt_string(zmq.SUBSCRIBE, self.topic)
        topic = ""
        content = ""
        try:
            if not self.context.closed and not self.receiver.closed:
                data = self.receiver.recv_multipart()
                topic = data[0].decode("utf-8")
                content = data[1].decode("utf-8")
                if self.handle:
                    self.log.info(f"执行主题={self.topic}的处理函数")
                    self.handle(topic, content)
        except zmq.ContextTerminated:
            self.log.error(f"zmq上下文对象已关闭")
        return topic, content

    # 关闭
    def close(self):
        self.log.info("close===========================")
        if self.sender:
            self.sender.close()
        if self.receiver:
            self.receiver.close()
            self.share_data.thread_event.set()
            self.is_run = False
        if self.context:
            self.context.term()
            # self.context.destory()

    def run(self):
        self.log.debug(f"监听主题={self.topic}的消息")
        while not self.share_data.thread_event.is_set() and self.is_run:
            self.log.debug(f"topic={self.topic}的订阅线程状态={self.share_data.thread_event.is_set()}")
            if self.run_type == 1:
                self.recv()
            if self.run_type == 2:
                self.recv_multi()
            if self.run_type == 3:
                self.recv_str()
            if self.run_type == 4:
                self.recv_multi_str()
        self.log.info(f"停止接收主题={self.topic}的消息")


class Server(BaseSocket):
    def __init__(self, host="localhost", port=5555):
        super().__init__()
        self.host = host
        self.port = port
        self.context = zmq.Context()
        # 创建一个PUB类型的套接字
        self.sender = self.context.socket(zmq.PUB)
        # 绑定套接字到指定的地址
        # 使用代理 所以这里改成connect方法  用来连接代理服务
        # self.sender.bind(f"tcp://{host}:{port}")
        self.sender.connect(f"tcp://{self.host}:{self.port}")


class Client(BaseSocket):
    def __init__(self, host="localhost", port=5555, topic="", run_type=4, handle=None):
        super().__init__()
        self.is_run = True
        self.run_type = run_type
        self.handle = handle
        self.host = host
        self.port = port
        self.topic = topic
        self.context = zmq.Context()
        # 创建一个PUB类型的套接字
        self.receiver = self.context.socket(zmq.SUB)
        # 绑定套接字到指定的地址
        self.receiver.connect(f"tcp://{self.host}:{self.port}")
        self.receiver.setsockopt_string(zmq.SUBSCRIBE, self.topic)
        # self.receiver.subscribe(zmq.SUBSCRIBE, self.topic)


def my_server():
    publisher = Server()
    publisher.send_multi("a", Message("a", {"aa": 1}, None))
    data_list = [
        {"a": "测试中文"},
        {"b": {"vv": "qweqe"}},
        {"c": {"aaaf": "这里时测试数据"}}
    ]
    for data in data_list:
        for k, v in data.items():
            publisher.send_multi(k, Message(k, v, None))
            time.sleep(1)


def my_client():
    subscriber1 = Client(topic="a")
    subscriber2 = Client(topic="b")
    # while True:
    data1 = subscriber1.recv_multi()
    print("接收消息=======1=======")
    print(data1)
    data2 = subscriber2.recv_multi()
    print("接收消息=======2=======")
    print(data2)


if __name__ == '__main__':
    print("Python")

    # publisher = Server()
    # publisher.send_multi("a", Message("a", {"aa": 1}, None))
    # data_list = [
    #     {"a": 1},
    #     {"b": {"vv"  "qweqe"}},
    #     {"c": {"aaaf"  "qweqe"}}
    # ]
    # for data in data_list:
    #     for k, v in data.items():
    #         publisher.send_multi(k, Message(k, v, None))
    #         time.sleep(1)

    # subscriber1 = Client(topic="a")
    # subscriber2 = Client(topic="b")
    # while True:
    #     data = subscriber1.recv_multi()
    #     print("接收消息==============")
    #     print(data)

    # 创建线程
    sender_thread = threading.Thread(target=my_server)
    receiver_thread = threading.Thread(target=my_client)

    # 启动线程
    sender_thread.start()
    receiver_thread.start()
